#include "OCamera.h"
#include "OUtil.h"
#include "DataFilter.h"

void OCamera::calViewMatrix() {
	auto cameraRotX = m_pitchAngle;
	auto transMat = glm::translate(glm::mat4(1), -m_position);
	auto rotXMat = glm::rotate(glm::mat4(1), -cameraRotX, glm::vec3(1, 0, 0));
	auto rotYMat = glm::rotate(glm::mat4(1), -m_yawAngle, glm::vec3(0, 1, 0));
	m_viewMatrix = rotXMat * rotYMat * transMat;
	m_viewMatrix *= glm::scale(glm::mat4(1), glm::vec3(m_viewScale));
	this->setViewMatrix(OUtil::toMatrix(m_viewMatrix));

	if (m_projectionMatrix != Mat_Zero)
		DataFilter::get()->updateCamera(m_viewMatrix, m_projectionMatrix);
}

void OCamera::calProjectionMatrix() {
	const auto Aspect = float(W3D) / H3D;
	const auto NearPlane = Config::Camera_NearPlane;
	const auto FarPlane = Config::Camera_FarPlane;
	m_projectionMatrix = glm::perspective(m_fov, Aspect, NearPlane, FarPlane);
	this->setProjectionMatrix(OUtil::toMatrix(m_projectionMatrix));

	if (m_viewMatrix != Mat_Zero)
		DataFilter::get()->updateCamera(m_viewMatrix, m_projectionMatrix);
}

osg::Vec3d OCamera::calculateWorldCoordinate(float screenX, float screenY) {
	// 1. Get viewport
	osg::Viewport* vp = this->getViewport();
	if (!vp) return osg::Vec3d();

	// 2. Convert to NDC coordinates
	double nx = (screenX / vp->width()) * 2.0 - 1.0;
	double ny = 1.0 - (screenY / vp->height()) * 2.0;

	// 3. Obtain the inverse transformation matrix
	osg::Matrixd VP = this->getViewMatrix() * this->getProjectionMatrix();
	osg::Matrixd invVP;
	invVP.invert(VP);

	// 4. Calculate points on the far plane
	osg::Vec3d farNDC(nx, ny, 1.0);
	osg::Vec3d farWorld = farNDC * invVP;

	// 5. Calculate points on the near plane
	osg::Vec3d nearNDC(nx, ny, -1.0);
	osg::Vec3d nearWorld = nearNDC * invVP;

	// 6. Calculate the intersection point with the XZ plane (Y=0)
	osg::Vec3d ray = farWorld - nearWorld;
	if (fabs(ray.y()) < 1e-6) return farWorld;

	double t = -nearWorld.y() / ray.y();
	osg::Vec3d result = nearWorld + ray * t;
	result.y() = 0.0;// force Y=0

	return result;
}